#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;
//class Person
//{
//public:
//	virtual void BuyTicket()
//	{
//		cout << "买票全价" << endl;
//	}
//};
//class Student : public Person
//{
//public :
//	virtual void BuyTicket()
//	{
//		cout << "买票半价" << endl;
//	}
//};
//void Func(Person& people)
//{
//	people BuyTicket();
//}
//void Test()
//{
//	Person Mike;
//	Func(Mike);
//	Student Johnson;
//	Func(Johnson);
//}
//class Person
//{
//public:
//	virtual void BuyTicket() {
//		cout << "买票全价" << endl;
//	}
//};
//int main()
//{
//
//	return 0;
//}

//class Person {
//public:
//	virtual void BuyTicket()
//	{
//		cout << "买票-全价" << endl; 
//	}
//};
//class Student : public Person {
//public:
//	virtual void BuyTicket()
//	{
//		cout << "买票-打折" << endl; 
//	}
//};
///*
//这里的virtual和菱形继承部分的virtual没有关系
//*/
//void Func(Person* ptr)
//{
//	// 这⾥可以看到虽然都是Person指针Ptr在调⽤BuyTicket
//	// 但是跟ptr没关系，而是由ptr指向的对象决定的。
//	ptr->BuyTicket();
//}
//传引用
//void Func(Person& ptr)
//{
//	// 这⾥可以看到虽然都是Person指针Ptr在调⽤BuyTicket
//	// 但是跟ptr没关系，而是由ptr指向的对象决定的。
//	ptr.BuyTicket();
//}
//int main()
//{
//	Person ps;
//	Student st;
//	Func(&ps);
//	Func(&st);
//
//	//也可以传引用,修改上面的Func函数即可
//	/*
//	Func(&ps);
//	Func(&st);
//	*/
//	return 0;
//}

//class Animal
//{
//public:
//	virtual void talk() const
//	{}
//};
//class Dog : public Animal
//{
//public:
//	virtual void talk()  const
//	{
//		std::cout << "汪汪" << std::endl;
//	}
//};
//class Cat : public Animal
//{
//public:
//	virtual void talk() const
//	{
//		std::cout << "(>^ω^<)喵" << std::endl; 
//	}
//};
//void letsHear(const Animal& animal)
//{
//	animal.talk();
//}
//int main()
//{
//	Cat cat;
//	Dog dog;
//	letsHear(cat);
//	letsHear(dog);
//	return 0;
//}

//class A
//{
//public:
//	virtual void func(int val = 1) { std::cout << "A->" << val << std::endl; }
//	virtual void test() { func(); }
//};
//class B : public A
//{
//public:
//	void func(int val = 0) { std::cout << "B->" << val << std::endl; }
//};
//int main(int argc, char* argv[])
//{
//	B bb;
//	bb.test();
//	return 0;
//}

//class A {};
//class B : public A {};
//class Person {
//public:
//	virtual A* BuyTicket()
//	{
//		cout << "买票-全价" << endl;
//		return nullptr;
//	}
//};
//class Student : public Person {
//public:
//	virtual B* BuyTicket()
//	{
//		cout << "买票-打折" << endl;
//		return nullptr;
//	}
//};
//void Func(Person* ptr)
//{
//	ptr->BuyTicket();
//}
//int main()
//{
//	Person ps;
//	Student st;
//	Func(&ps);
//	Func(&st);
//	return 0;
//}
/*
虽然不满足三同,但是必须满足协变,但必须是父子类的关系。
可以是当前类的父子类,也可以是其他类的父子类
*/

//class A
//{
//public:
//	virtual ~A()
//	{
//		cout << "~A()" << endl;
//	}
//};
//class B : public A {
//public:
//	virtual ~B()
//	{
//		cout << "~B()->delete:" << _p << endl;
//		delete _p;
//	}
//protected:
//	int* _p = new int[10];
//};
////只有派生类Student的析构函数重写了Person的析构函数，下面的delete
////对象调用析构函数，才能构成多态，才能保证p1和p2指向的对象正确的调用析构函数。
//int main()
//{
//	A* p1 = new A;
//	A* p2 = new B;
//	//只有派生类Student的析构函数重写了Person的析构函数，下面的delete对象调⽤析构函数，才能
//	//构成多态，才能保证p1和p2指向的对象正确的调用析构函数。
//	delete p1;
//	delete p2;
//	return 0;
//}
/*
B继承了A之后,如果不加virtual肯定不构成重写,不构成重写这里有一个问题,
父类的指针可以指向父类的对象,也可以指向子类的对象,如果第二个时子类的
指针,这里不会有问题,父类指针指向父类对象,子类指针指向子类对象,这个时候
没有问题,delete p1会调用A的析构函数,delete p2会调用B的析构函数,B的析构
函数结束了之后会自动调用A的析构函数.因为派生类的析构函数不需要去显示调用
父类的析构函数,因为它为了保证先子后父的这样的顺序,但是就像上面这样写,父类
的指针,既指向父类,又指向子类的对象,然后去调用虚函数,实现多态,那么这里就会
出现问题。
第一个p1去调用A的析构函数没什么问题,那这个P2指向的是B,它应该去调用B的析构函
数,但是现在没有调到,没调到就会导致B对象没有释放,但这种情况其实是不会的,因为
new和delete,new开空间会调用operator new,operator new会调用malloc,然后调用构造
函数初始化,delete会调用析构函数,析构函数会完成清理工作,但是B对象释放了,里面
的资源没有释放,因为没有调B的析构函数,所以这个地方就会导致内存泄露,指向父类
调用的是父类的析构函数,指向子类,调用的也是父类的析构函数。
那么怎么样才能做到正确调用,没有内存泄露呢?
第一步:调用析构函数,析构函数被特殊处理过,所以调用的是p2->destructor
第二步:operator delete(p2),实际就是free。
如果这里第一句调用形成一个多态的话就没问题了,operatr delete没有问题,
但是p1调用destructor的时候就形成多态,
*/

//class A
//{
//public:
//	virtual ~A()
//	{
//		cout << "~A()" << endl;
//	}
//};
//class B : public A {
//public:
//	virtual ~B() override
//	{
//		cout << "~B()->delete:" << _p << endl;
//		delete _p;
//	}
//protected:
//	int* _p = new int[10];
//};
//int main()
//{
//	A* p1 = new A;
//	A* p2 = new B;
//	delete p1;
//	delete p2;
//	return 0;
//}

// error C3248: “Car::Drive”: 声明为“final”的函数⽆法被“Benz::Drive”重写
//class Car
//{
//public:
//	virtual void Drive() final {}
//};
//class Benz :public Car
//{
//public:
//	virtual void Drive() {
//		cout << "Benz舒适" << endl; }
//};
//int main()
//{
//	return 0;
//}

//抽象类
//class Car
//{
//public:
//	//纯虚函数,虚函数后面写上赋值0,可以写上实现,但是一般都不写。
//	virtual void Drive() = 0;
//};
//class Benz :public Car
//{
//public:
//	virtual void Drive()
//	{
//		cout << "Benz舒适" << endl;
//	}
//};
//class BMW :public Car
//{
//public:
//	virtual void Drive()
//	{
//		cout << "BMW操控" << endl;
//	}
//};
//int main()
//{
//	// 编译报错：error C2259 : “Car”:无法实例化抽象类
//	//Car car;
//	Benz b;//也是抽象类,虽然没有直接包含纯虚函数,但是继承了以后间接包含,
//	//凡是包含纯虚函数的类都是抽象类
//
//	/*不能实例化出对象,但定义指针是没问题的,这个时候就可以实现出多态
//	所以不影响他实现出多态的*/
//	Car* pBenz = new Benz;
//	pBenz->Drive();
//	Car* pBMW = new BMW;
//	pBMW->Drive();
//
//	return 0;
//}
/*
抽象类不能实例化出对象,比如一个系统中定义一个类,我们不希望这个类
可以实例化出对象,那么这个时候就可以加上纯虚函数,变成抽象类。那么
就得必须重写,重写了以后我的这个函数就不是纯虚函数了,那么就不是
抽象类,就可以实例化出对象了,某种程度上说纯虚函数强制类派生类重写。
1.纯虚函数强制了派生类的重写。
2.如果某个类不想实例化出对象,抽象的表达,在显示中没有实体,或者说定义
出实体也没多大意义,那就可以把这个类定义成包含纯虚函数的抽象类。
*/

//class Base
//{
//public:
//	virtual void Func1()
//	{
//		cout << "Func1()" << endl;
//	}
//	virtual void Func2()
//	{
//		cout << "Func2()" << endl;
//	}
//	virtual void Func3()
//	{
//		cout << "Func3()" << endl;
//	}
//protected:
//	int _b = 1;
//	char _ch = 'x';
//};
//int main()
//{
//	Base b;
//	cout << sizeof(b) << endl;
//	return 0;
//}
/*
这里的_b是int类型,占4个字节,_ch是char类型,占1个字节,总共占5个字节,然后内存对齐,一共占
8个字节,但实际上不是8个字节,因为还多了一个虚函数表指针,
*/

//class Person {
//public:
//	virtual void BuyTicket() {
//		cout << "买票-全价" << endl; 
//	}
//	virtual void func1() {
//		cout << "func1" << endl;
//	}
//private:
//	string _name;
//	int _age;
//};
//class Student : public Person {
//public:
//	virtual void BuyTicket() {
//		cout << "买票-打折" << endl; 
//	}
//private:
//	string _id;
//};
//void Func(Person* ptr)
//{
//	//这里可以看到虽然都是Person指针Ptr在调用BuyTicket
//	// 但是跟ptr没关系，而是由ptr指向的对象决定的。
//	ptr->BuyTicket();
//}
//int main()
//{
//	// 其次多态不仅仅发生在派⽣类对象之间，多个派⽣类继承基类，重写虚函数后
//	// 多态也会发生在多个派生类之间。
//	Person ps;
//	Student st;
//	Soldier sr;
//
//	Func(&ps);
//	Func(&st);
//	Func(&sr);
//	return 0;
//}
/*
Person的里面有一个_name,_age还有一个虚表的指针,虚表的指针指向这张表上面
有个虚函数,派生类由两部分构成,一部分是自己的成员,一部分是父类的成员,子类
里面包含的父类里面也有一个虚表,这个虚表和父类中的虚表不是同一张表,他们正真
的不同在虚表指向的存储的这个虚函数,父类的里面存储的这个虚函数是父类的地址,
子类里面存储的是重写的虚函数的地址。
如果有个没有被重写的虚函数,它也在派生类里面,
*/

/*父类里面放的是父类的虚函数, 派生类里面放的是派生类重写的虚函数,
那么当这里完成多态调用的时候,怎么做到指向谁调用谁的呢?
*/

//class Person {
//public:
//	virtual void BuyTicket() {
//		cout << "买票-全价" << endl; 
//	}
//};
//class Student : public Person {
//public:
//	virtual void BuyTicket() {
//		cout << "买票-打折" << endl; 
//	}
//};
//class Soldier : public Person {
//public:
//	virtual void BuyTicket() {
//		cout << "买票-优先" << endl; 
//	}
//};
//void Func(Person* ptr)
//{
//	//这里可以看到虽然都是Person指针Ptr在调用BuyTicket
//	//但是跟ptr没关系，而是由ptr指向的对象决定的。
//	ptr->BuyTicket();
//}
//int main()
//{
//	//其次多态不仅仅发⽣在派生类对象之间，多个派⽣类继承基类，重写虚函数后
//	//多态也会发⽣在多个派生类之间。
//	Person ps;
//	Student st;
//	Soldier sr;
//	Func(&ps);
//	Func(&st);
//	Func(&sr);
//	return 0;
//}
/*
虚函数的重写是为了让父类对象和子类对象当中的虚表当中分别存对应的虚函数的地址,
必须是父类的指针或者引用去调用是因为只有父类的指针或引用指向父类对象,也可以
指向子类对象,既可以指向父类对象,也可以指向子类对象,指向父类对象的时候调用父类
对象中的虚表当中的虚函数,指向子类对象的时候是把子类对象中父类的那一部分给切出
来,都是去父类对象中的虚表中找,有可能这个父类对象本身就是一个父类对象,也有可能
是子类当中切出的父类对象。
运行时多态:运行起来以后到虚表里面去找对应的虚函数。
*/


//class Person {
//public:
//	virtual void BuyTicket() {cout << "买票-全价" << endl; }
//	virtual void func1() {cout << "func1" << endl;}
//	virtual void func2() {cout << "func2" << endl;}
//	int _a1 = 1;
//	int _a2 = 2;
//};
//class Student : public Person {
//public:
//	virtual void BuyTicket() {cout << "买票-打折" << endl; }
//	virtual void func1() { cout << "func1" << endl; }
//	virtual void func3() { cout << "func3" << endl; }
//	int _a3 = 3;
//};
//int main()
//{
//	Person p1;
//	Person p2;
//	Person p3;
//	Student s;
//	return 0;
//}

/*
如果没有完成重写,也没有自己的虚函数,那么子类的虚函数表跟父类是不是同一张虚函数表呢?
刚才完成重写,要覆盖对应的位置,肯定得新搞一张虚函数表,但是限制没有重写,也没有新增的虚函数,那
也不是同一张虚函数表,哪怕没有进行重写,跟父类也用的不是同一张虚函数表,因为这样的话好区分,
*/

/*
不同的父类和子类用的是不同的虚函数表,同一个类型用的是同一个虚函数表
虚函数表是同类型的对象共享的,
*/

class Base {
public:
	virtual void func1() { cout << "Base::func1" << endl; }
	virtual void func2() { cout << "Base::func2" << endl; }
	void func5() { cout << "Base::func5" << endl; }
protected:
	int a = 1;
};

class Derive : public Base
{
public:
	// 重写基类的func1
	virtual void func1() { cout << "Derive::func1" << endl; }
	virtual void func3() { cout << "Derive::func1" << endl; }
	void func4() { cout << "Derive::func4" << endl; }
protected:
	int b = 2;
};
int main()
{
	int i = 0;
	static int j = 1;
	int* p1 = new int;
	const char* p2 = "xxxxxxxx";
	printf("栈: % p\n", &i);
	printf("静态区: % p\n", &j);
	printf("堆: % p\n", p1);
	printf("常量区: % p\n", p2);
	Base b;
	//现在有一个对象b，怎么才能打印出虚表的地址。
	Derive d;
	Base* p3 = &b;
	Derive* p4 = &d;
	printf("Base虚表地址: % p\n", *(int*)p3);
	printf("Derive虚表地址: % p\n", *(int*)p4);
	printf("虚函数地址: % p\n", &Base::func1);//取成员函数的地址要加&
	printf("普通函数地址: % p\n", &Base::func5);
	return 0;
}
